///*
// * Copyright 2002-2021 the original author or authors.
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// *      https://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
//package io.github.icodegarden.beecomb.common.util.spring;
//
//import java.time.DateTimeException;
//import java.time.temporal.Temporal;
//import java.time.temporal.ValueRange;
//
//import io.github.icodegarden.commons.lang.annotation.Nullable;
///**
// * full copy spring 5.3.10
// */
//
///**
// * Efficient bitwise-operator extension of {@link CronField}.
// * Created using the {@code parse*} methods.
// *
// * @author Arjen Poutsma
// * @since 5.3
// */
//final class BitsCronField extends CronField {
//
//	private static final long MASK = 0xFFFFFFFFFFFFFFFFL;
//
//
//	@Nullable
//	private static BitsCronField zeroNanos = null;
//
//
//	// we store at most 60 bits, for seconds and minutes, so a 64-bit long suffices
//	private long bits;
//
//
//	private BitsCronField(Type type) {
//		super(type);
//	}
//
//	/**
//	 * Return a {@code BitsCronField} enabled for 0 nano seconds.
//	 */
//	public static BitsCronField zeroNanos() {
//		if (zeroNanos == null) {
//			BitsCronField field = new BitsCronField(Type.NANO);
//			field.setBit(0);
//			zeroNanos = field;
//		}
//		return zeroNanos;
//	}
//
//	/**
//	 * Parse the given value into a seconds {@code BitsCronField}, the first entry of a cron expression.
//	 */
//	public static BitsCronField parseSeconds(String value) {
//		return parseField(value, Type.SECOND);
//	}
//
//	/**
//	 * Parse the given value into a minutes {@code BitsCronField}, the second entry of a cron expression.
//	 */
//	public static BitsCronField parseMinutes(String value) {
//		return BitsCronField.parseField(value, Type.MINUTE);
//	}
//
//	/**
//	 * Parse the given value into a hours {@code BitsCronField}, the third entry of a cron expression.
//	 */
//	public static BitsCronField parseHours(String value) {
//		return BitsCronField.parseField(value, Type.HOUR);
//	}
//
//	/**
//	 * Parse the given value into a days of months {@code BitsCronField}, the fourth entry of a cron expression.
//	 */
//	public static BitsCronField parseDaysOfMonth(String value) {
//		return parseDate(value, Type.DAY_OF_MONTH);
//	}
//
//	/**
//	 * Parse the given value into a month {@code BitsCronField}, the fifth entry of a cron expression.
//	 */
//	public static BitsCronField parseMonth(String value) {
//		return BitsCronField.parseField(value, Type.MONTH);
//	}
//
//	/**
//	 * Parse the given value into a days of week {@code BitsCronField}, the sixth entry of a cron expression.
//	 */
//	public static BitsCronField parseDaysOfWeek(String value) {
//		BitsCronField result = parseDate(value, Type.DAY_OF_WEEK);
//		if (result.getBit(0)) {
//			// cron supports 0 for Sunday; we use 7 like java.time
//			result.setBit(7);
//			result.clearBit(0);
//		}
//		return result;
//	}
//
//
//	private static BitsCronField parseDate(String value, BitsCronField.Type type) {
//		if (value.equals("?")) {
//			value = "*";
//		}
//		return BitsCronField.parseField(value, type);
//	}
//
//	private static BitsCronField parseField(String value, Type type) {
//		Assert.hasLength(value, "Value must not be empty");
//		Assert.notNull(type, "Type must not be null");
//		try {
//			BitsCronField result = new BitsCronField(type);
//			String[] fields = StringUtils.delimitedListToStringArray(value, ",");
//			for (String field : fields) {
//				int slashPos = field.indexOf('/');
//				if (slashPos == -1) {
//					ValueRange range = parseRange(field, type);
//					result.setBits(range);
//				}
//				else {
//					String rangeStr = field.substring(0, slashPos);
//					String deltaStr = field.substring(slashPos + 1);
//					ValueRange range = parseRange(rangeStr, type);
//					if (rangeStr.indexOf('-') == -1) {
//						range = ValueRange.of(range.getMinimum(), type.range().getMaximum());
//					}
//					int delta = Integer.parseInt(deltaStr);
//					if (delta <= 0) {
//						throw new IllegalArgumentException("Incrementer delta must be 1 or higher");
//					}
//					result.setBits(range, delta);
//				}
//			}
//			return result;
//		}
//		catch (DateTimeException | IllegalArgumentException ex) {
//			String msg = ex.getMessage() + " '" + value + "'";
//			throw new IllegalArgumentException(msg, ex);
//		}
//	}
//
//	private static ValueRange parseRange(String value, Type type) {
//		if (value.equals("*")) {
//			return type.range();
//		}
//		else {
//			int hyphenPos = value.indexOf('-');
//			if (hyphenPos == -1) {
//				int result = type.checkValidValue(Integer.parseInt(value));
//				return ValueRange.of(result, result);
//			}
//			else {
//				int min = Integer.parseInt(value.substring(0, hyphenPos));
//				int max = Integer.parseInt(value.substring(hyphenPos + 1));
//				min = type.checkValidValue(min);
//				max = type.checkValidValue(max);
//				if (type == Type.DAY_OF_WEEK && min == 7) {
//					// If used as a minimum in a range, Sunday means 0 (not 7)
//					min = 0;
//				}
//				return ValueRange.of(min, max);
//			}
//		}
//	}
//
//	@Nullable
//	@Override
//	public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
//		int current = type().get(temporal);
//		int next = nextSetBit(current);
//		if (next == -1) {
//			temporal = type().rollForward(temporal);
//			next = nextSetBit(0);
//		}
//		if (next == current) {
//			return temporal;
//		}
//		else {
//			int count = 0;
//			current = type().get(temporal);
//			while (current != next && count++ < CronExpression.MAX_ATTEMPTS) {
//				temporal = type().elapseUntil(temporal, next);
//				current = type().get(temporal);
//				next = nextSetBit(current);
//				if (next == -1) {
//					temporal = type().rollForward(temporal);
//					next = nextSetBit(0);
//				}
//			}
//			if (count >= CronExpression.MAX_ATTEMPTS) {
//				return null;
//			}
//			return type().reset(temporal);
//		}
//	}
//
//	boolean getBit(int index) {
//		return (this.bits & (1L << index)) != 0;
//	}
//
//	private int nextSetBit(int fromIndex) {
//		long result = this.bits & (MASK << fromIndex);
//		if (result != 0) {
//			return Long.numberOfTrailingZeros(result);
//		}
//		else {
//			return -1;
//		}
//
//	}
//
//	private void setBits(ValueRange range) {
//		if (range.getMinimum() == range.getMaximum()) {
//			setBit((int) range.getMinimum());
//		}
//		else {
//			long minMask = MASK << range.getMinimum();
//			long maxMask = MASK >>> - (range.getMaximum() + 1);
//			this.bits |= (minMask & maxMask);
//		}
//	}
//
//	private void setBits(ValueRange range, int delta) {
//		if (delta == 1) {
//			setBits(range);
//		}
//		else {
//			for (int i = (int) range.getMinimum(); i <= range.getMaximum(); i += delta) {
//				setBit(i);
//			}
//		}
//	}
//
//	private void setBit(int index) {
//		this.bits |= (1L << index);
//	}
//
//	private void clearBit(int index) {
//		this.bits &=  ~(1L << index);
//	}
//
//	@Override
//	public int hashCode() {
//		return Long.hashCode(this.bits);
//	}
//
//	@Override
//	public boolean equals(Object o) {
//		if (this == o) {
//			return true;
//		}
//		if (!(o instanceof BitsCronField)) {
//			return false;
//		}
//		BitsCronField other = (BitsCronField) o;
//		return type() == other.type() && this.bits == other.bits;
//	}
//
//	@Override
//	public String toString() {
//		StringBuilder builder = new StringBuilder(type().toString());
//		builder.append(" {");
//		int i = nextSetBit(0);
//		if (i != -1) {
//			builder.append(i);
//			i = nextSetBit(i+1);
//			while (i != -1) {
//				builder.append(", ");
//				builder.append(i);
//				i = nextSetBit(i+1);
//			}
//		}
//		builder.append('}');
//		return builder.toString();
//	}
//
//}
